﻿/*
 
Celerity: Sensory Overload
Copright 2012 by Adam Hill, David Gannon, Thomas Tamblyn & Patrick Ytting
Contact: adam.hill@outlook.com
Website: adamhill.org
Licence: MS-Pl Licence
  
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Celerity.World.Particles
{
	public class ParticleFactory
	{
		private Effect particleEffect;
		private VertexPositionColorTexture[] vertices;
		private short[] indices;
		private float deltaPhase;
		private float currentPhase;

		//containers
		private int particleCount; // must be incremented when particle created and decremented when particle is destroyed
		public int ParticleCount { get { return particleCount; } }
		List<ExplosionParticle> explosionParticles;

		public ParticleFactory(GraphicsDeviceManager graphicsDeviceManager, Effect particleEffect)
		{
			this.particleEffect = particleEffect;
			this.particleCount = 0;
			this.deltaPhase = 0.0f;
			explosionParticles = new List<ExplosionParticle>();
		}

		private void BuildGeometry(Camera camera)
		{
			vertices = new VertexPositionColorTexture[explosionParticles.Count * 4];
			indices = new short[explosionParticles.Count * 6];
			int vertexCount = 0;
			int indexCount = 0;
			int particleCount = 0;

			//TODO: set texture coords here using atlas technique
			foreach (ExplosionParticle ep in explosionParticles)
			{
				Matrix billboard = Matrix.CreateScale(ep.size) * Matrix.CreateBillboard(ep.postion, camera.position, camera.up, Vector3.Normalize(camera.cameraLookAt));
				ParticleQuad quad = new ParticleQuad();
				foreach (VertexPositionTexture vpt in quad.vertices)
				{
					vertices[vertexCount].Position = Vector3.Transform(vpt.Position, billboard);
					vertices[vertexCount].Color = ep.colour;
					vertexCount++;
				}
				for(int i = 0; i <= quad.indices.GetUpperBound(0); i++)
				{
					indices[indexCount] = (short)(quad.indices[i] + quad.vertices.Count() * particleCount);
					indexCount++;
				}
				particleCount++;
			}
		}

		public void Update(GameTime gameTime, Camera camera, float currentPhase)
		{
			deltaPhase = currentPhase - this.currentPhase;
			this.currentPhase = currentPhase;
			foreach (ExplosionParticle ep in explosionParticles)
				ep.Update(gameTime, deltaPhase);

			CullParticles();
		}

		public void Draw(GraphicsDevice graphicsDevice, Matrix world, Matrix projection, Camera camera)
		{
			if (this.particleCount > 0)
			{
				BuildGeometry(camera);

				particleEffect.CurrentTechnique = particleEffect.Techniques["Technique1"];
				particleEffect.Parameters["World"].SetValue(world);
				particleEffect.Parameters["Projection"].SetValue(projection);
				particleEffect.Parameters["View"].SetValue(camera.view);
				particleEffect.CurrentTechnique.Passes[0].Apply();
				graphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, this.vertices, 0, this.vertices.Count(), this.indices, 0, this.vertices.Count() / 2);
			}
		}

		private void CullParticles()
		{
			//TODO: add code for other particles here
			for (int i = 0; i < explosionParticles.Count; i++)
			{
				if (!explosionParticles[i].isAlive)
				{
					explosionParticles.RemoveAt(i);
					particleCount--;
				}
			}
		}

		#region ExplosionParticles
		public void CreateExplosionParticle(Vector3 postion, Vector3 velocity, Color colour)
		{
			ExplosionParticle ep = new ExplosionParticle()
			{
				airResistance = 10.0f,
				acceleration = new Vector3(0.0f, 0.0f, 0.0f),
				angularVelocity = 0.0f,
				atlasCoord = new Vector2(0.0f, 0.0f),
				colour = colour,
				isAlive = true,
				mass = 1.0f,
				postion = postion,
				rotation = 0.0f,
				size = 0.5f,
				velocity = velocity
			};
			explosionParticles.Add(ep);
			particleCount++;
		}
		#endregion ExplosionParticles

	}
}
